Skip to content

reply install: keep the CLI current without the user touching npm - #14

Merged
ArtemKosolap merged 11 commits into
mainfrom
dev/REPLY-51593-self-update
Aug 2, 2026
Merged

reply install: keep the CLI current without the user touching npm#14
ArtemKosolap merged 11 commits into
mainfrom
dev/REPLY-51593-self-update

Conversation

@ArtemKosolap

Copy link
Copy Markdown
Contributor

Adds reply install — one command that keeps a user's CLI current without them ever recalling an npm incantation — and a quiet hint from reply --version when a newer release exists.

$ reply install
  0.4.0 → 0.5.0, updating with npm…
✓ reply 0.4.0 → 0.5.0 installed

$ reply install
✓ reply 0.5.0 is the newest release

reply update is an accepted alias of the same command, undocumented on purpose: users learn one name, and muscle memory from other CLIs still works.

npm remains the only way to install the CLI, and the README now says the Node requirement plainly, next to the install command, instead of leaving it to be discovered from an npm error.

Only a global npm install is ours to drive

Everything else is reported with the command that fits it, and nothing is spawned:

how it was installed what install does
npm, globally runs npm install -g <pkg>@latest and reports old → new
inside a project prints npm install <pkg>@latest, names the project
through npx explains npx already resolves the newest on every run
from a checkout prints git pull && npm ci && npm run build
unrecognised layout prints the public command and names the directory it judged

Global versus project-local is decided by which directory owns the node_modules holding us, and whether that directory is the working directory or an ancestor of it — the way node itself resolves. Asking only whether the module sits under cwd gets it backwards for a version-manager install: ~/.nvm/…/node_modules is under $HOME, so a user standing in their home directory would have had their global install called project-local and the update refused for a project that isn't there. That case has a regression test.

Which package we are running as decides the channel, and the two are never crossed — sending a public-channel user to GitHub Packages would point them at a registry they cannot read.

What it compares against

The GitHub Releases API, for both channels. The tag is the version of record: package.json in this repository is deliberately 0.0.0-development. One code path, no token, and no npm subprocess just to ask a question. It is safe for the public channel because the publish workflow flips a release to latest only after npm publish succeeds, so a hit there means npm already has it; the internal channel compares against the newest release of any kind, which is exactly the pre-release stream.

The check may not tax the CLI

reply --version is the only command that can check, and only when a human is watching. It is suppressed — and no request is made at all — under --json/--pretty, --quiet, a non-TTY stderr, CI or GITHUB_ACTIONS, REPLY_NO_UPDATE_CHECK=1, and in a source checkout. The answer is cached for a day, a failure backs off for an hour, the request aborts after 1.5 s, and every error is silent. Verified by hand: with output piped, no request is made and no cache file is even created.

Exit codes: 0 when updated or already current, 1 whenever an update exists and was not applied — so reply install --dry-run works as a CI check.

Safety

  • npm is only ever invoked as npm install -g <package>@latest, and only for one of the two packages we publish — a guard inside the one function that can spawn a process, not a convention.
  • npm's output is status, so it is relayed to stderr; --json stdout carries the report and nothing else.
  • A permission failure reports what npm said and offers sudo … — or, on Windows where there is nothing to prepend, says to use an elevated terminal.
  • Nothing here writes to an install it does not manage. There is no --yes that runs npm for a copy we did not verify.

Verification

531 offline tests (up from 421), plus tsc. No test performs a request, spawns npm, or touches a real config directory — fetch, the clock, env, cwd and the npm runner are all injected.

Checked by hand against a staged package tree: the global path, the project-local path, --dry-run, --json, and the permission-denied branch on both platform behaviours.

Not in this change

Native binaries, bootstrap scripts, PATH wiring and a Node-less install. Node stays a requirement, stated up front. That work is written up separately and deliberately waits for real demand from users who cannot install Node.

@ArtemKosolap
ArtemKosolap merged commit 4963ad1 into main Aug 2, 2026
6 checks passed
@ArtemKosolap
ArtemKosolap deleted the dev/REPLY-51593-self-update branch August 2, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants